

#include "rtmp_message.h"
#include "amf.h"
#include "byte_process.h"
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <endian.h>
#include <netinet/in.h>


RtmpMessageHeader::RtmpMessageHeader()
{
    /* m_body = nullptr; */    
    m_stream_id = 0;
    m_type_id   = 0;
    m_body_size = 0;
    m_duration  = 0;
    m_rtmp_body = nullptr;
}

void RtmpMessageHeader::ComposeHeader(uint8_t * data, int length, int & use_length)
{
    if (m_body_size == 0 && m_rtmp_body)
    {
        m_body_size = m_rtmp_body->BodySize();
    }
    int index = 0;
    switch(m_format)    
    {
        case 0:
        {
            data[index++] = (m_format << 6)|m_chunk_stream_id;
            memcpy(data + index, &m_time, 3);
            index += 3;
            /* printf("compose body size : %d.\n", m_body_size); */
            data[index + 2] = m_body_size & 0xff;
            data[index + 1] = m_body_size >> 8;
            data[index + 0] = m_body_size >> 16;
            index += 3; 
            data[index++] = m_type_id;
            m_stream_id = 0;
            memcpy(data + index, &m_stream_id, sizeof(m_stream_id));
            index += 4;
            use_length = index;
            break;
        }
        case 1:
        {
            data[index++] = (m_format << 6)|m_chunk_stream_id;
            memcpy(data + index, &m_time, 3);
            index += 3;
            /* printf("compose body size : %d.\n", m_body_size); */
            data[index + 2] = m_body_size & 0xff;
            data[index + 1] = m_body_size >> 8;
            data[index + 0] = m_body_size >> 16;
            index += 3; 
            data[index++] = m_type_id;
            use_length = index;
            break;
        }
        case 2:
        {
            break;
        }
        case 3:
        {
            break;
        }
        default:
            break;
    }
}

void RtmpMessageHeader::ComposeDataToBuffer(uint8_t * data, int length, int & use_length)
{
    use_length = 0; 
    ComposeHeader(data, length, use_length);
    if (m_rtmp_body)
    {
        int body_size = 0;
        /* m_body->ComposeBodyData(data + use_length, length - use_length, body_size); */
        m_rtmp_body->ComposeBodyData(data + use_length, length - use_length, body_size);
        use_length += body_size;
    }
}


RtmpMessageBody::RtmpMessageBody()
{}

RtmpMessageBody::~RtmpMessageBody()
{
    for (auto & data : m_content_vec)
    {
        delete data;
    }
    m_content_vec.clear();
}
bool RtmpMessageHeader::ParseRtmpMsgHeader(const uint8_t * data, int data_length, int & use_length)
{
    int index = 0;
    m_format = data[index] & 0b11000000;
    m_chunk_stream_id = data[index++] & 0b00111111;
    memcpy(&m_time, data + index, 3);
    /* m_time = htonl(m_time); */
    m_time = HToBeN((uint8_t*)&m_time, sizeof(m_time), 3);
    index += 3;
    memcpy(&m_body_size, data + index, 3);
    m_body_size = BeToHn((uint8_t*)&m_body_size, sizeof(m_body_size), 3); 
    index += 3;
    m_type_id = data[index++];
    memcpy(&m_stream_id, data + index, sizeof(m_stream_id));
    index += 4;
    use_length = index;
    return true;
}


ParserRtmpMsgStatus RtmpMessageBody::ParseRtmpMsgBody(const uint8_t * data, int data_length, int body_size, int & use_index)
{
    ParserRtmpMsgStatus status;
    int index = 0;
    for (index = 0; index < body_size;)
    {
        int length = 0;
        auto value = AmfValue::ParseBuffer((data + index), body_size - index, length);
        if (!value)
        {
            status = EmRtmpMsgError;
            break;
        }
        if (value->GetValueType() == START_OBJECT)
        {
            auto obj = (AmfObject*)value;
            std::string content = obj->Dump();
            /* printf("object data |%s|.\n", content.c_str()); */
        }
        if (value->GetValueType() == NUMBER)
        {
            auto num = (AmfNumber*)value;
            auto n = num->GetValue();
            /* printf("number data : %lf.\n", n); */
        }
        m_content_vec.push_back(value);
        index += length;
    }
    status = status != EmRtmpMsgError ? EmRtmpMsgSuccess : EmRtmpMsgError;
    printf("content data size : %ld and status : %d.\n", m_content_vec.size(), status);
    return status;
}

void RtmpMessageBody::ComposeBodyData(uint8_t * data, int length, int & use_length)
{
    int index = 0;
    int length_compute = 0;
    for (auto & value : m_content_vec)
    {
        index += value->CopyToBuffer(data + index, length - index);
        length_compute += value->GetLength();
    }
    /* printf("compose body length : %d and size : %ld and compute : %d.\n", index, m_content_vec.size(), length_compute); */
    use_length = index;
}

double RtmpMessageBody::GetNumberValue()
{
    double value = 0;
    for (auto & data : m_content_vec)
    {
        if (data && data->GetValueType() == NUMBER)
        {
            auto number = (AmfNumber*)data;
            value = number->GetValue();
        }
    }
    return value;
}

std::string RtmpMessageBody::GetFirstStr()
{
    std::string result;
    for (auto & data : m_content_vec)
    {
        if (data && data->GetValueType() == STRING)
        {
            result = ((AmfString*)data)->GetValue();
            break;
        }
    }
    return result;
}

int RtmpMessageBody::BodySize()
{
    int length = 0;
    for (auto & data : m_content_vec)
    {
        length += data->GetLength();
    }
    return length;
}

void RtmpMessageBody::AddAmfValue(AmfValue * value)
{
    m_content_vec.push_back(value);
}

RtmpPacketMsg::RtmpPacketMsg()
{
}

int RtmpPacketMsg::PushData(const uint8_t * data, int length)
{
    int index = 0;
    if (length >= 1)
    {
        uint8_t format   = (data[index] & 0b11000000) >> 6;
        uint8_t chunk_id = (data[index++] & 0x0f);
        if (chunk_id == 0)
        {
            // rtmp basic header has 2byte   
            if (length >= 2)
            {
                chunk_id = data[index++] + 64;   
            }
            else
            {

            }
        }
        else if (chunk_id == 1)
        {
            // rtmp basic header has 3byte
            if (length >= 3)
            {
                int one_byte = index++;
                int two_byte = index++;
                chunk_id = ((data[one_byte] << 8) + data[two_byte]); 
            }
            else
            {

            }
        }
        printf("format : %d\tchunk stream id : %d.\n", format, chunk_id);
        int header_size = format == 0 ? 12 : (format == 1 ? 8 : (format == 2 ? 4 : 1));
        printf("rtmp message header size : %d.\n", header_size);
        if (header_size <= length)       
        {
            if (header_size >= 1)
            {
                m_header.m_format = format;
                m_header.m_chunk_stream_id = chunk_id;
                if (header_size >= 4)
                {
                    memcpy(&m_header.m_time, data + index, 3);
                    m_header.m_time = BeToHn((uint8_t*)&m_header.m_time, 4, 3);
                    index += 3;
                }
                if (header_size >= 8) 
                {
                    memcpy(&m_header.m_body_size, data + index, 3);
                    m_header.m_body_size = BeToHn((uint8_t*)&m_header.m_body_size, 4, 3);
                    index += 3;
                    m_header.m_type_id = data[index++];
                }
                if (header_size >= 12)
                {
                    memcpy(&m_header.m_stream_id, data + index, 4);
                    index += 4;
                }
            }
            int chunk_data_size = 128;
            int body_size = m_header.m_body_size; 
            while (length >= m_header.m_body_size + header_size)
            {
                printf("length : %d and body_size : %d and index : %d.\n", length, body_size, index);
                if (chunk_data_size > body_size)
                {
                    chunk_data_size = body_size;
                }
                for (int i = 0; i < chunk_data_size; i++) 
                {
                    m_body_data.push_back(*(data +index + i));
                }
                index += chunk_data_size;
                body_size -= chunk_data_size;
                /* uint8_t header = data[index]; */
                /* format = (header & 0xc0) >> 6; */
                if (body_size != 0)
                {
                    // mutile chunk take mesage
                    if (length > index)
                    {
                        index++; 
                    }
                    else
                    {
                        // remove all
                        index = length;
                        break;
                    }
                }
                else if (m_header.m_type_id == 0x04)
                {
                    break;
                }
                else if (m_header.m_type_id == 0x05)
                {
                    uint32_t win_ack_size = 0;
                    memcpy(&win_ack_size, &*m_body_data.begin(), sizeof(win_ack_size));
                    win_ack_size = be32toh(win_ack_size);
                    printf("window ack size : %u.\n", win_ack_size);
                    break;
                }
                else if (m_header.m_type_id == 0x12)
                {
                    auto body = new RtmpPacketMsg();
                }
                else
                {
                    auto body = new RtmpMessageBody();
                    int use_length = 0;
                    int body_size  = m_header.m_body_size;
                    auto status = body->ParseRtmpMsgBody(&*m_body_data.begin(), body_size, body_size, use_length);
                    if (status != EmRtmpMsgSuccess)
                    {
                        printf("parse body status : %d,\n", status);
                        delete body;
                        body = nullptr;
                    }
                    else
                    {
                        m_header.m_rtmp_body = std::shared_ptr<RtmpMessageBody>(body);    
                    }
                    break;
                }
            }
        }
        else
        {
            index = 0;
        }
    }
    else
    {
        index = 0;
    }
    return index;
}

bool RtmpPacketMsg::IsComplete()
{
    if (m_header.m_type_id == 0x05 || m_header.m_type_id == 0x04)
    {
        return true;
    }
    return m_header.m_rtmp_body!=nullptr;
}

void RtmpPacketMsg::AddRtmpMsg(RtmpMessageHeader & msg)
{
    /* m_rtmp_msg_vec.push_back(msg); */
    RtmpPacketMsg rtmp_msg;
    rtmp_msg.m_header = msg;
    m_rtmp_msg_vec.push_back(rtmp_msg);
}


